/* 
 *  @author -- Lateef Yusuf
 *  @email -- iamlateef@gatech.edu
 *  @date -- Jul 21, 2010 
 */
package com.simplegdatamaps;

/* Interface with google servers to add maps, delete and update
 * map features and 
 * search over maps for range queries
 * */

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GoogleMapsClient {

	private String auth;
	private String password;
	private String username;
	private String userId;

	public String getAuth() {
		return auth;
	}

	public String getUserId() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public String getUsername() {
		return username;
	}

	public GoogleMapsClient(String user, String passwd) {
		username = user;
		password = passwd;
		retrieveAuthToken(username, password);
		setUserId();
	}

	private void retrieveAuthToken(String username, String passwd) {
		String[] keys = { "accountType", "Email", "Passwd", "service", "source" };
		String[] values = { "GOOGLE", username, passwd,
				Constants.GOOGLEMAPS_SERVICE, Constants.GOOGLEMAPS_SOURCE };
		String data = URLConnection.createURLParams(keys, values);
		String urlResponse = URLConnection.postRequest(
				Constants.GDATA_AUTH_TOKEN, data);
		String[] resValues = urlResponse.split("=");
		auth = resValues[3];
	}

	private void setUserId() {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_GET_ALL_MAPS,
				username);
		InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
				auth);
		String doc = URLConnection.writeToString(urlResponse);
		if (doc == null) {
			userId = username; 
			return;
		}
		Pattern p = Pattern.compile("<id>.{39}.{1,30}</id>");
		Matcher m = p.matcher(doc);
		if (m.find()) {
			String k = m.group();
			String[] vals = k.split("<id>.{39}");
			String id = vals[1].substring(0, vals[1].length() - 5);
			userId = id;
		}
	}

	public String getMap(String title) {
		List<String> maps = getAllMaps();
		int len = maps.size();
		for (int i=0; i < len; i++) {
			String map = maps.get(i);
			String urlStr = String.format(Constants.GOOGLEMAPS_FEED_GET_MAP,
					username, map);
			InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
					auth);
			String doc = URLConnection.writeToString(urlResponse);
			if (doc == null) return null;
			Pattern p = Pattern.compile("<title>.+</title>");
			Matcher m = p.matcher(doc);
			if (m.find()) {
				String k = m.group();
				int x = k.indexOf("<title>");
				int y = k.indexOf("</title>");
				String tit = k.substring(x + 7, y);
				if (tit.equals(title))
					return map;
			}
		}
		return null;
	}

	public List<String> getAllMaps() {
		ArrayList<String> maps = new ArrayList<String>();
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_GET_ALL_MAPS,
				username);
		InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
				auth);
		String doc = URLConnection.writeToString(urlResponse);
		if (doc == null) return maps;
		Pattern p = Pattern.compile("<id>.{39}.{1,30}/.{1,30}</id>");
		Matcher m = p.matcher(doc);
		while (m.find()) {
			String k = m.group();
			String[] vals = k.split("<id>.{39}.{1,30}/");
			String id = vals[1].substring(0, vals[1].length() - 5);
			maps.add(id);
		}
		return maps;
	}

	// unique titles
	public String addMap(String title, String description) {
		String map = getMap(title);
		if (map != null) return map;
		
		String urlStr = String
				.format(Constants.GOOGLEMAPS_FEED_ADD_MAP, userId);
		String data = GDataAtomXMLBuilder.getAddMapXML(title, description);
		InputStream urlResponse = URLConnection.postAuthorizedRequest(urlStr,
				data, auth, URLConnection.RequestType.POST);
		String doc = URLConnection.writeToString(urlResponse);
		Pattern p = Pattern.compile("<id>.{39}.{1,30}/.{1,30}</id>");
		Matcher m = p.matcher(doc);
		if (m.find()) {
			String k = m.group();
			String[] vals = k.split("<id>.{39}.{1,30}/");
			String id = vals[1].substring(0, vals[1].length() - 5);
			return id;
		}
		return null;
	}

	public void deleteMap(String mapId) {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_DELETE_MAP,
				username, mapId);
		URLConnection.postAuthorizedRequest(urlStr, "", auth,
				URLConnection.RequestType.DELETE);
	}

	public List<Placemark> getAllLocation(String mapId) {
		String urlStr = String.format(
				Constants.GOOGLEMAPS_FEED_GET_ALL_FEATURES, userId, mapId);
		InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
				auth);
		List<Placemark> marks =new ArrayList<Placemark>();
		try {
			String doc = URLConnection.writeToString(urlResponse);
			marks = parseFeaturesKML(doc);
		} catch (Exception e) {
		}
		return marks;
	}

	// unique titles
	public Placemark getLocationInfo(String title, String name,
			String mapId) {
		List<Placemark> locs = getAllLocation(mapId);
		int len = locs.size();
		for (int i = 0; i < len; i++) {
			Placemark p = locs.get(i);
			if (p.title.equals(title))
				return p;
		}
		return null;
	}

	public String addLocationInfo(String title, String name,
			String mapId, double latitude, double longitude) {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_ADD_FEATURE,
				userId, mapId);
		String data = GDataAtomXMLBuilder.getAddLocationFeatureXML(title,
				name, latitude, longitude);
		InputStream urlResponse = URLConnection.postAuthorizedRequest(urlStr,
				data, auth, URLConnection.RequestType.POST);
		String doc = URLConnection.writeToString(urlResponse);
		Pattern p = Pattern
				.compile("<atom:id>.{43}.{1,30}/.{1,30}/.{1,30}</atom:id>");
		Matcher m = p.matcher(doc);
		if (m.find()) {
			String k = m.group();
			String[] vals = k.split("<atom:id>.{43}.{1,30}/.{1,30}/");
			String id = vals[1].substring(0, vals[1].length() - 10);
			return id;
		}
		return null;
	}

	public void updateLocationInfo(String title, String mapId,
			String featureId, double latitude, double longitude) {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_GET_FEATURE,
				userId, mapId, featureId);
		InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
				auth);
		String feature = URLConnection.writeToString(urlResponse);
		if (feature == null) return;
		int index = feature.indexOf("<Point><coordinates>");
		StringBuilder data = new StringBuilder();
		if (index != -1) {
			data.append(feature.substring(0, index));
			data.append("<Point><coordinates>");
			data.append(latitude);
			data.append(",");
			data.append(longitude);
			data.append(",0.0");
			int index2 = feature.indexOf("</coordinates></Point>", index);
			if (index2 > -1) {
				data.append(feature.substring(index2, feature.length()));
				urlResponse = URLConnection.postAuthorizedRequest(urlStr, data
						.toString(), auth, URLConnection.RequestType.PUT);
			}

		}
	}

	public void deleteLocationInfo(String mapId, String featureId) {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_DELETE_FEATURE,
				userId, mapId, featureId);
		URLConnection.postAuthorizedRequest(urlStr, "", auth,
				URLConnection.RequestType.DELETE);
	}

	public List<Placemark> searchMap(String mapId, double latitude,
			double longitude, double radius) {
		String urlStr = String.format(Constants.GOOGLEMAPS_FEED_SEARCH_MAP,
				userId, mapId, latitude, longitude, radius);
		InputStream urlResponse = URLConnection.getAuthorizedRequest(urlStr,
				auth);
		List<Placemark> marks = new ArrayList<Placemark>();
		try {
			String doc = URLConnection.writeToString(urlResponse);
			marks = parseFeaturesKML(doc);
		} catch (Exception e) {
		}
		return marks;
	}

	private List<Placemark> parseFeaturesKML(String doc) {
		ArrayList<Placemark> locations = new ArrayList<Placemark>();
		Pattern p = Pattern
				.compile("<atom:id>.{43}.{1,30}/.{1,30}/.{1,30}</atom:id>");
		Matcher m = p.matcher(doc);
		while (m.find()) {
			try {
				String k = m.group();
				String[] vals = k.split("<atom:id>.{43}.{1,30}/.{1,30}/");
				String id = vals[1].substring(0, vals[1].length() - 10);

				int start = m.end();
				String restDoc = doc.substring(start);
				int i = 0;
				int j = 0;
				Pattern p1 = Pattern.compile("<atom:title>.+</atom:title>");
				Matcher m1 = p1.matcher(restDoc);
				String title = "";
				if (m1.find()) {
					k = m1.group();
					i = k.indexOf("<atom:title>");
					j = k.indexOf("</atom:title>");
					title = k.substring(i+12, j);
				}
				
				Pattern p2 = Pattern.compile("<Placemark><name>.+</name>");
				Matcher m2 = p2.matcher(restDoc);
				String name = "";

				if (m2.find()) {
					k = m2.group();
					i = k.indexOf("<name>");
					j = k.indexOf("</name>");
					name = k.substring(i + 6, j);
				}
				

				Pattern p3 = Pattern.compile("<coordinates>.+</coordinates>");
				Matcher m3 = p3.matcher(restDoc);
				String location = "";
				if (m3.find()) {
					k = m3.group();
					i = k.indexOf("<coordinates>");
					j = k.indexOf("</coordinates>");
					location = k.substring(i + 13, j);
				}
				Placemark place = new Placemark();
				place.featureId = id;
				place.title = title;
				place.name = name;
				vals = location.split(",");
				place.latitude = Double.parseDouble(vals[0]);
				place.longitude = Double.parseDouble(vals[1]);
				locations.add(place);
			} catch (Exception e) {
			}
		}
		return locations;
	}

}
